Introduction
This document will serve as a tutorial for using SCISSORS, with the added functionality of detailing exactly how the PBMC3k dataset figures presented in our manuscript were generated. We’ll start from a 10X counts matrix and end with fully annotated cell clusters. In addition to R, in order to run all the code in this document you’ll need a Python 3 installation with openTSNE and all its dependencies installed.
Libraries
R
library(dplyr) # tidy data
library(Seurat) # single cell infrastructure
library(ggplot2) # plots
library(SCISSORS) # reclustering
library(ggsignif) # significance bars
library(paletteer) # advanced colors
library(latex2exp) # LaTeX
library(reticulate) # Python interface
library(ggbeeswarm) # beeswarm plots
library(SeuratData) # PBMC3k dataset
Python
import numpy as np
from openTSNE import TSNEEmbedding
from openTSNE import initialization
from openTSNE.affinity import Multiscale
from openTSNE.affinity import PerplexityBasedNN
Data
We load a scRNA-seq dataset provided by 10X Genomics that consists of 2,700 peripheral blood mononuclear cells (PBMCs) from a healthy donor. We remove all cells with unique feature counts greater than 2,500 in order to prevent doublets/multiplets from being included in the dataset.
pbmc <- LoadData("pbmc3k")
pbmc <- subset(pbmc, subset = nFeature_RNA < 2500)
Preprocessing
Here we use PrepareData() to normalize expression & select highly variable genes through sctransform, run PCA & t-SNE. and cluster our cells. We utilize 15 principal components even though the Satija Lab vignette used 10, as we’re using sctransform normalization, which does a better job of retaining biological heterogeneity through normalization than standard log-normalization.
pbmc <- PrepareData(pbmc,
n.HVG = 4000,
regress.cc = FALSE,
regress.mt = FALSE,
n.PC = 15,
which.dim.reduc = "tsne",
initial.resolution = .4,
random.seed = 629)
## [1] "Running t-SNE on 15 principal components with perplexity = 30"
## [1] "Found 6 unique clusters"
We see clear visual evidence of subclusters.
p0a <- DimPlot(pbmc, pt.size = 1.75, cols = paletteer_d("ggthemes::Classic_10")) +
labs(x = "t-SNE 1", y = "t-SNE 2") +
theme_yehlab() +
guides(color = guide_legend(nrow = 1, override.aes = list(size = 4)))
p0a

We compute the distribution of the silhouette scores for each cluster.
sil_df <- ComputeSilhouetteScores(pbmc, avg = FALSE)
p0b <- ggplot(sil_df, aes(x = Cluster, y = Score, fill = Cluster)) +
geom_violin(draw_quantiles = 0.5, color = "black", scale = "width") +
scale_fill_paletteer_d("ggthemes::Classic_10") +
labs(y = "Silhouette Score", x = "Louvain Clusters", fill = NULL) +
theme_minimal() +
theme(panel.grid = element_blank(),
panel.border = element_rect(fill = NA, size = 1),
legend.position = "none",
axis.ticks = element_line(),
axis.title.x = element_text(size = 24),
axis.title.y = element_text(size = 24),
axis.text.x = element_text(size = 20),
axis.text.y = element_text(size = 20))
Clusters 0, 1, and 2 have statistically significantly lower silhouette scores than the other three clusters. We also see a small clump of cells in Cluster 4 that has lower scores than the rest of the cluster. We’ll focus on these four clusters when reclustering, as clusters 3 and 5 seem mostly correct.

Fit-SNE
We’ll also run the Fast Fourier Transform-accelerated version of t-SNE as implemented in the Python library openTSNE. First we’ll need to get our PC matrix into a form accessible by our Python interpreter.
pc_mat <- Embeddings(pbmc, reduction = "pca")
# import PC matrix
pc_mat = np.array(r.pc_mat)
# run Fit-SNE w/ multiscale kernel
init = initialization.pca(pc_mat, random_state=629)
affin_anneal = PerplexityBasedNN(pc_mat, perplexity=50, metric='cosine', random_state=629)
tsne = TSNEEmbedding(init, affin_anneal, negative_gradient_method='fft')
embed1 = tsne.optimize(n_iter=250, exaggeration=10, momentum=0.6)
embed2 = embed1.optimize(n_iter=750, exaggeration=1, momentum=0.8)
affin_anneal.set_perplexity(20)
embed3 = embed2.optimize(n_iter=50, exaggeration=1, momentum=0.8)
embed4 = embed3.optimize(n_iter=350)
Now we pull the results back into R, and save them in our Seurat object. We save the original embedding (made using the default Barnes-Hut approximate t-SNE implementation) in pbmc@reduction$bh_tsne - we need to do this in order to make the Fit-SNE embedding the default embedding that will be retrieved in calls to functions such as DimPlot() or FeaturePlot().
embed <- as.matrix(py$embed4)
rownames(embed) <- colnames(pbmc)
colnames(embed) <- c("Fit-SNE_1", "Fit-SNE_2")
pbmc@reductions$bh_tsne <- pbmc@reductions$tsne
pbmc@reductions$tsne <- CreateDimReducObject(embeddings = embed,
key = "FitSNE_",
assay = "SCT",
global = TRUE)
Visualizing the results shows pretty much the same global structure as with the default t-SNE implementation, albeit rotated a bit, but I like that Fit-SNE’s clusters are a bit denser, so we’ll use Fit-SNE going forward.
p1 <- DimPlot(pbmc, pt.size = 1.75, cols = paletteer_d("ggthemes::Classic_10")) +
labs(x = "Fit-SNE 1", y = "Fit-SNE 2") +
theme_yehlab() +
guides(color = guide_legend(nrow = 1, override.aes = list(size = 4)))
p1

Broad Cell Types
Next we’ll run a differential expression test in order to assign general labels to our clusters. These identities will help guide our reclustering.
pbmc_de <- FindAllMarkers(pbmc,
logfc.threshold = .5,
test.use = "wilcox",
only.pos = TRUE,
random.seed = 629,
verbose = FALSE)
pbmc_de %>%
filter(p_val_adj < 0.05) %>%
group_by(cluster) %>%
top_n(n = 5, wt = avg_log2FC) -> top5_de
From the top 5 differentially expressed genes for each cluster, we can deduce some basic cluster identities. Clusters 0 & 2 look like T cells. Clusters 1 & 4 appear to be monocytes. Cluster 3 strongly expresses B cell markers. Lastly, Cluster 5 is pretty clearly identifiable as being composed of NK cells. When we recluster our cells, we’ll keep the biological context of these broad cell types in mind.
p2 <- DotPlot(pbmc, features = unique(top5_de$gene), dot.scale = 15) +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
labs(color = "Expression", size = "% Expressed", y = "Cluster") +
theme(axis.text.x = element_text(angle = 90, size = 16, vjust = 0.5),
legend.position = "right",
legend.justification = "center",
panel.border = element_rect(fill = NA, size = 1, color = "black"),
axis.line = element_blank(),
legend.title = element_text(size = 18),
axis.title.x = element_blank(),
axis.title.y = element_text(size = 20),
axis.text.y = element_text(size = 18)) +
guides(color = guide_colorbar(title.position = "top", barheight = unit(3, units = "cm"), title.hjust = 0.5),
size = guide_legend(title.position = "top", title.hjust = 0.5))
p2

Reclustering
We’ll run SCISSORS on the monocytes & T cells; i.e. on clusters 1 & 4 and 0 & 2. We use slightly different possible parameter sets as the T cell object is much larger than the monocyte object.
mono_reclust <- ReclusterCells(pbmc,
which.clust = list(1, 4),
merge.clusters = TRUE,
n.HVG = 4000,
n.PC = 15,
k.vals = c(10, 20, 30),
resolution.vals = c(.3, .4, .5),
redo.embedding = TRUE,
random.seed = 629)
t_reclust <- ReclusterCells(pbmc,
which.clust = list(0, 2),
merge.clusters = TRUE,
n.HVG = 4000,
n.PC = 15,
k.vals = c(30, 40, 50),
resolution.vals = c(.3, .4, .5),
redo.embedding = TRUE,
random.seed = 629)
## [1] "Reclustering cells in clusters 1, 4 using k = 10 & resolution = 0.5; S = 0.438"
## [1] "Reclustering cells in clusters 0, 2 using k = 50 & resolution = 0.3; S = 0.308"
Now we’ll run Fit-SNE on each of the reclustered objects for consistencies sake. First we’ll need to create variables containing the PC matrices and send them to Python.
pc_mono <- Embeddings(mono_reclust, reduction = "pca")
pc_t <- Embeddings(t_reclust, reduction = "pca")
We run Fit-SNE.
# import PC matrices
pc_mono = np.array(r.pc_mono)
pc_t = np.array(r.pc_t)
# run Fit-SNE - Monocytes
init_mono = initialization.pca(pc_mono, random_state=629)
affin_anneal_mono = PerplexityBasedNN(pc_mono, perplexity=50, metric='cosine', random_state=629)
tsne_mono = TSNEEmbedding(init_mono, affin_anneal_mono, negative_gradient_method='fft')
embed1_mono = tsne_mono.optimize(n_iter=250, exaggeration=12, momentum=0.6)
embed2_mono = embed1_mono.optimize(n_iter=950, exaggeration=1, momentum=0.8)
# run Fit-SNE - T cells
init_t = initialization.pca(pc_t, random_state=629)
affin_anneal_t = PerplexityBasedNN(pc_t, perplexity=50, metric='cosine', random_state=629)
tsne_t = TSNEEmbedding(init_t, affin_anneal_t, negative_gradient_method='fft')
embed1_t = tsne_t.optimize(n_iter=250, exaggeration=12, momentum=0.6)
embed2_t = embed1_t.optimize(n_iter=950, exaggeration=1, momentum=0.8)
Now we bring the results back in to R.
embed_mono <- as.matrix(py$embed2_mono)
rownames(embed_mono) <- colnames(mono_reclust)
mono_reclust@reductions$bh_tsne <- mono_reclust@reductions$tsne
mono_reclust@reductions$tsne <- CreateDimReducObject(embeddings = embed_mono,
key = "FitSNE_",
assay = "SCT",
global = TRUE)
embed_t <- as.matrix(py$embed2_t)
rownames(embed_t) <- colnames(t_reclust)
t_reclust@reductions$bh_tsne <- t_reclust@reductions$tsne
t_reclust@reductions$tsne <- CreateDimReducObject(embeddings = embed_t,
key = "FitSNE_",
assay = "SCT",
global = TRUE)
Here’s what our reclusterings look like. There’s clear visual separation between the main clusters and the subgroups we’ve discovered using SCISSORS.
p3 <- DimPlot(mono_reclust, pt.size = 1.75, cols = paletteer_d("ggsci::default_nejm")) +
labs(x = "Fit-SNE 1", y = "Fit-SNE 2") +
theme_yehlab() +
guides(color = guide_legend(nrow = 1, override.aes = list(size = 4)))
p4 <- DimPlot(t_reclust, pt.size = 1.75, cols = paletteer_d("ggsci::default_nejm")) +
labs(x = "Fit-SNE 1", y = "Fit-SNE 2") +
theme_yehlab() +
guides(color = guide_legend(nrow = 1, override.aes = list(size = 4)))
p3


Next, we’ll reintegrate our new clusters into our original Seurat object - this requires some finagling as Seurat is a bit weird with how it stores cell-level metadata. Since we had six clusters originally, and we discovered six new subclusters, we’ll end up with twelve total clusters.
pbmc <- IntegrateSubclusters(original.object = pbmc,
reclust.results = list(mono_reclust, t_reclust))
p5 <- DimPlot(pbmc, pt.size = 1.75, cols = paletteer_d("ggthemes::Classic_10")) +
labs(x = "Fit-SNE 1", y = "Fit-SNE 2") +
theme_yehlab() +
guides(color = guide_legend(nrow = 1, override.aes = list(size = 4)))
p5

Identify Cell Types
Now that we’ve determined our subpopulations, we can assign cell types to each cluster using the marker genes provided in the Satija Lab’s PBMC3k vignette, as well as other canonical markers.
CD4+ T Cells
We can quickly identify cluster 7 as the naive CD4+ T cells, and cluster 8 as the memory CD4+ population.
p6 <- FeaturePlot(pbmc, pt.size = 1.75, features = "IL7R") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p7 <- FeaturePlot(pbmc, pt.size = 1.75, features = "CCR7") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p8 <- FeaturePlot(pbmc, pt.size = 1.75, features = "S100A4") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
(p6 | p7 | p8) / p5

CD14+ Monocytes
Cluster 2 clearly houses our CD14+ monocytes.
p9 <- FeaturePlot(pbmc, pt.size = 1.75, features = "CD14") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p10 <- FeaturePlot(pbmc, pt.size = 1.75, features = "LYZ") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
(p9 | p10) / p5

FCGR3A+ Monocytes
The FCGR3A+ monocytes are positioned near the CD14+ monocytes in cluster 4. Note: these are also known as CD16+ monocytes.
p11 <- FeaturePlot(pbmc, pt.size = 1.75, features = "FCGR3A") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p12 <- FeaturePlot(pbmc, pt.size = 1.75, features = "MS4A7") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
(p11 | p12) / p5

B Cells
Expression of MS4A1 allows us to isolate the B cells in cluster 0.
p16 <- FeaturePlot(pbmc, pt.size = 1.75, features = "MS4A1") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p16 / p5

CD8+ T Cells
The canonical marker CD8A swiftly identifies our CD8+ T cells in cluster 9.
p17 <- FeaturePlot(pbmc, pt.size = 1.75, features = "CD8A") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p17 / p5

Natural Killer Cells
We can use NKG7 and GNLY to isolate the NK cells in cluster 1.
p18 <- FeaturePlot(pbmc, pt.size = 1.75, features = "NKG7") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p19 <- FeaturePlot(pbmc, pt.size = 1.75, features = "GNLY") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
(p18 | p19) / p5

Dendritic Cells
The dendritic cell group is defined by expression of FCER1A and CST3 in cluster 5.
p20 <- FeaturePlot(pbmc, pt.size = 1.75, features = "FCER1A") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p21 <- FeaturePlot(pbmc, pt.size = 1.75, features = "CST3") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
(p20 | p21) / p5

Platelets
The tiny platelet population of 14 cells can be identified by its expression of PPBP in cluster 6.
p22 <- FeaturePlot(pbmc, pt.size = 1.75, features = "PPBP") +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
theme_yehlab() +
NoLegend() +
theme(axis.title = element_blank())
p22 / p5

Final Figure
Finally, we’ll add cell labels to our original Seurat object and plot the results.
pbmc$label <- case_when(pbmc$seurat_clusters == 0 ~ "B",
pbmc$seurat_clusters == 1 ~ "NK",
pbmc$seurat_clusters == 2 ~ "CD14+ Monocyte",
pbmc$seurat_clusters == 3 ~ "Intermediate Monocyte",
pbmc$seurat_clusters == 4 ~ "FCGR3A+ Monocyte",
pbmc$seurat_clusters == 5 ~ "DC",
pbmc$seurat_clusters == 6 ~ "Platelet",
pbmc$seurat_clusters == 7 ~ "Naive CD4+ T",
pbmc$seurat_clusters == 8 ~ "Memory CD4+ T",
pbmc$seurat_clusters == 9 ~ "CD8+ T")
pbmc$label <- factor(pbmc$label, levels = c("B", "NK", "CD14+ Monocyte", "Intermediate Monocyte",
"FCGR3A+ Monocyte", "DC", "Platelet", "Naive CD4+ T",
"Memory CD4+ T", "CD8+ T"))
Idents(pbmc) <- "label"
We visualize the final cells labels for our 11 clusters.
p23 <- DimPlot(pbmc, pt.size = 1.75, cols = paletteer_d("ggthemes::Classic_10")) +
theme_yehlab() +
guides(color = guide_legend(nrow = 3, override.aes = list(size = 4))) +
labs(x = "Fit-SNE 1", y = "Fit-SNE 2", title = NULL)
p23

We perform a final differential expression analysis for our cell types.
final_de <- FindAllMarkers(pbmc,
logfc.threshold = .5,
test.use = "wilcox",
only.pos = TRUE,
verbose = FALSE,
random.seed = 629)
final_de %>%
filter(p_val_adj < 0.05) %>%
group_by(cluster) %>%
top_n(n = 5, wt = avg_log2FC) -> top_de_final
p24 <- DotPlot(pbmc, features = unique(top_de_final$gene), dot.scale = 15) +
scale_color_gradientn(colors = paletteer_d("wesanderson::Zissou1")) +
labs(color = "Expression", size = "% Expressed", y = NULL) +
theme(axis.text.x = element_text(angle = 90, size = 16, vjust = 0.5),
legend.position = "right",
legend.justification = "center",
panel.border = element_rect(fill = NA, size = 1, color = "black"),
axis.line = element_blank(),
legend.title = element_text(size = 18),
axis.title.x = element_blank(),
axis.title.y = element_text(size = 20),
axis.text.y = element_text(size = 18)) +
guides(color = guide_colorbar(title.position = "top", barheight = unit(3, units = "cm"), title.hjust = 0.5),
size = guide_legend(title.position = "top", title.hjust = 0.5))
Our differential expression results confirm our manual annotations. Each of the celltypes expresses some of its canonical markers, so we can be pretty confident that we got the annotations right.

Conclusions
SCISSORS automatically split a large CD4+ T cluster into naive and memory CD4+ T cells. It successfully separated a small dendritic cell cluster from the CD14+ monocytes it had originally been grouped with, and teased out the intermediate monocyte population nestled between the CD14+ and CD16+ monocyte clusters. Lastly, it identified a tiny platelet cluster that had been erroneously grouped with the CD16+ monocytes. The intermediate monocytes were not annotated in the original Satija Lab PBMC3k vignette.
We used the PBMC3k dataset because of 1) its immediate availability to anyone wishing to replicate our results and 2) the validity of its annotations, which allowed us to be confident in the results from SCISSORS, which was able to carve out rare cell groups from larger, broader cell types. In this case, the dendritic cell cluster was composed of 37 cells, and the platelet cluster of just 14 cells. However, SCISSORS isn’t just useful for rare cell types: it also identified the intermediate monocytes, a cluster of 191 cells. We thus believe we can confidently say that SCISSORS has been shown to accurately and swiftly identify cell types, both common and rare, by considering the variance in gene expression within clusters and judging iterative reclustering using silhouette scores. We put forth that this approach is theoretically advantageous for identifying rare cell populations, rather than attempting to do so at the level of the entire dataset.
Save Data & Figures
This part isn’t really worth reading; it’s just here to prove that all the figures were actually dynamically generated and saved upon knitting this document.
First we save the finalized Seurat object.
saveRDS(pbmc, file = "~/Desktop/Data/pbmc3k.Rds")
We’ll also save the final DE genes as an Excel spreadsheet.
openxlsx::write.xlsx(final_de, file = "~/Desktop/Data/PBMC_DE.xlsx")
We’ll create a quick convenience function to help us save the figures.
SaveFigure <- function(my.plot = NULL, name = NULL, height = 8, width = 8) {
if (is.null(plot) | is.null(name)) stop("You forgot some arguments.")
# save figure as is - w/ axis labels, titles, etc.
dir <- "~/Desktop/R/SCISSORS/vignettes/figures_supp/PBMC"
ggsave(my.plot,
filename = paste0(name, ".pdf"),
device = "pdf",
units = "in",
path = dir,
height = height,
width = width)
# save "blank" figure w/ no labels, legends, etc.
dir <- "~/Desktop/R/SCISSORS/vignettes/figures_pub/PBMC"
plot_blank <- my.plot +
theme(axis.title = element_blank(),
panel.border = element_blank(),
plot.title = element_blank(),
plot.subtitle = element_blank(),
legend.position = "none")
ggsave(plot_blank,
filename = paste0(name, ".pdf"),
device = "pdf",
units = "in",
path = dir,
height = height,
width = width)
}
Lastly, we’ll save the figures under ./vignettes/figures/.
SaveFigure(p0a, name = "Seurat_Clusters")
SaveFigure(p0b, name = "Seurat_Clusters_Silhouettes")
SaveFigure(p1, name = "Seurat_Clusters_FitSNE")
SaveFigure(p2, name = "Marker_Genes_For_Seurat_Clusters", height = 8, width = 18)
SaveFigure(p3, name = "Monocyte_Cluster_SCISSORS")
SaveFigure(p4, name = "T_Cluster_SCISSORS")
SaveFigure(p5, name = "SCISSORS_Clusters_Final")
SaveFigure(p6, name = "CD4T_IL7R")
SaveFigure(p7, name = "CD4T_CCR7")
SaveFigure(p8, name = "CD4T_S100A4")
SaveFigure(p9, name = "Monocyte_CD14")
SaveFigure(p10, name = "Monocyte_LYZ")
SaveFigure(p11, name = "FCGR3A_Monocyte_FCGR3A")
SaveFigure(p12, name = "FCGR3A_Monocyte_MS4A7")
SaveFigure(p13, name = "Intermediate_Mono_HLADPB1")
SaveFigure(p14, name = "Intermediate_Mono_CD74")
SaveFigure(p15, name = "Intermediate_Mono_HLADRA")
SaveFigure(p16, name = "B_MS4A1")
SaveFigure(p17, name = "CD8T_CD8A")
SaveFigure(p18, name = "NK_NKG7")
SaveFigure(p19, name = "NK_GNLY")
SaveFigure(p20, name = "DC_FCER1A")
SaveFigure(p21, name = "DC_CST3")
SaveFigure(p22, name = "Platelet_PPBP")
SaveFigure(p23, name = "SCISSORS_Celltypes_Final")
SaveFigure(p24, name = "Marker_Genes_For_SCISSORS_Celltypes", height = 8, width = 18)
And of course:
## R version 4.0.4 (2021-02-15)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Big Sur 10.16
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## attached base packages:
## [1] parallel stats4 stats graphics grDevices utils datasets
## [8] methods base
##
## other attached packages:
## [1] pbmc3k.SeuratData_3.1.4 SeuratData_0.2.1
## [3] ggbeeswarm_0.6.0 reticulate_1.18
## [5] latex2exp_0.5.0 paletteer_1.3.0
## [7] ggsignif_0.6.1 SCISSORS_0.0.2.0
## [9] SingleCellExperiment_1.12.0 SummarizedExperiment_1.20.0
## [11] Biobase_2.50.0 GenomicRanges_1.42.0
## [13] GenomeInfoDb_1.26.7 IRanges_2.24.1
## [15] S4Vectors_0.28.1 BiocGenerics_0.36.0
## [17] MatrixGenerics_1.2.1 matrixStats_0.58.0
## [19] data.table_1.14.0 cluster_2.1.1
## [21] biomaRt_2.46.3 ggplot2_3.3.3
## [23] SeuratObject_4.0.0 Seurat_4.0.1
## [25] dplyr_1.0.5
##
## loaded via a namespace (and not attached):
## [1] BiocFileCache_1.14.0 plyr_1.8.6 igraph_1.2.6
## [4] lazyeval_0.2.2 splines_4.0.4 listenv_0.8.0
## [7] scattermore_0.7 digest_0.6.27 htmltools_0.5.1.1
## [10] fansi_0.4.2 phateR_1.0.7 magrittr_2.0.1
## [13] memoise_2.0.0 tensor_1.5 ROCR_1.0-11
## [16] openxlsx_4.2.3 limma_3.46.0 globals_0.14.0
## [19] askpass_1.1 spatstat.sparse_2.0-0 prettyunits_1.1.1
## [22] colorspace_2.0-0 blob_1.2.1 rappdirs_0.3.3
## [25] ggrepel_0.9.1 xfun_0.22 prismatic_1.0.0
## [28] RCurl_1.98-1.3 crayon_1.4.1 jsonlite_1.7.2
## [31] spatstat.data_2.1-0 survival_3.2-10 zoo_1.8-9
## [34] glue_1.4.2 polyclip_1.10-0 gtable_0.3.0
## [37] zlibbioc_1.36.0 XVector_0.30.0 leiden_0.3.7
## [40] DelayedArray_0.16.3 future.apply_1.7.0 abind_1.4-5
## [43] scales_1.1.1 DBI_1.1.1 miniUI_0.1.1.1
## [46] Rcpp_1.0.6 viridisLite_0.4.0 xtable_1.8-4
## [49] progress_1.2.2 spatstat.core_2.0-0 bit_4.0.4
## [52] htmlwidgets_1.5.3 httr_1.4.2 RColorBrewer_1.1-2
## [55] ellipsis_0.3.1 ica_1.0-2 farver_2.1.0
## [58] pkgconfig_2.0.3 XML_3.99-0.6 sass_0.3.1
## [61] uwot_0.1.10 dbplyr_2.1.1 deldir_0.2-10
## [64] utf8_1.2.1 labeling_0.4.2 tidyselect_1.1.0
## [67] rlang_0.4.10 reshape2_1.4.4 later_1.1.0.1
## [70] AnnotationDbi_1.52.0 munsell_0.5.0 tools_4.0.4
## [73] cachem_1.0.4 cli_2.4.0 generics_0.1.0
## [76] RSQLite_2.2.6 ggridges_0.5.3 evaluate_0.14
## [79] stringr_1.4.0 fastmap_1.1.0 yaml_2.2.1
## [82] goftest_1.2-2 rematch2_2.1.2 knitr_1.32
## [85] bit64_4.0.5 fitdistrplus_1.1-3 zip_2.1.1
## [88] purrr_0.3.4 RANN_2.6.1 pbapply_1.4-3
## [91] future_1.21.0 nlme_3.1-152 mime_0.10
## [94] xml2_1.3.2 rstudioapi_0.13 compiler_4.0.4
## [97] beeswarm_0.3.1 plotly_4.9.3 curl_4.3
## [100] png_0.1-7 spatstat.utils_2.1-0 tibble_3.1.1
## [103] bslib_0.2.4 stringi_1.5.3 highr_0.8
## [106] lattice_0.20-41 Matrix_1.3-2 vctrs_0.3.7
## [109] pillar_1.6.0 lifecycle_1.0.0 spatstat.geom_2.1-0
## [112] lmtest_0.9-38 jquerylib_0.1.3 RcppAnnoy_0.0.18
## [115] bitops_1.0-6 cowplot_1.1.1 irlba_2.3.3
## [118] httpuv_1.5.5 patchwork_1.1.1 R6_2.5.0
## [121] promises_1.2.0.1 KernSmooth_2.23-18 gridExtra_2.3
## [124] vipor_0.4.5 parallelly_1.24.0 codetools_0.2-18
## [127] MASS_7.3-53.1 assertthat_0.2.1 openssl_1.4.3
## [130] withr_2.4.2 sctransform_0.3.2 GenomeInfoDbData_1.2.4
## [133] mgcv_1.8-34 hms_1.0.0 grid_4.0.4
## [136] rpart_4.1-15 tidyr_1.1.3 rmarkdown_2.7
## [139] Rtsne_0.15 shiny_1.6.0
LS0tCnRpdGxlOiAiUmVjbHVzdGVyaW5nIFBCTUNzIFVzaW5nIFNDSVNTT1JTIgpzdWJ0aXRsZTogIkphY2sgTGVhcnkiCmF1dGhvcjogCiAgLSAiVW5pdmVyc2l0eSBvZiBOb3J0aCBDYXJvbGluYSBhdCBDaGFwZWwgSGlsbCAtIExpbmViZXJnZXIgQ29tcHJlaGVuc2l2ZSBDYW5jZXIgQ2VudGVyIgogIC0gIlVuaXZlcnNpdHkgb2YgRmxvcmlkYSAtIERlcGFydG1lbnQgb2YgQmlvc3RhdGlzdGljcyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBwYXBlcgogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgZGZfcHJpbnQ6IGthYmxlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQpyZXRpY3VsYXRlOjp1c2VfdmlydHVhbGVudigifi9EZXNrdG9wL1B5dGhvbi9zY2llbmNlL3ZlbnYvIiwgcmVxdWlyZWQgPSBUUlVFKQpzZXQuc2VlZCg2MjkpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgZG9jdW1lbnQgd2lsbCBzZXJ2ZSBhcyBhIHR1dG9yaWFsIGZvciB1c2luZyBgU0NJU1NPUlNgLCB3aXRoIHRoZSBhZGRlZCBmdW5jdGlvbmFsaXR5IG9mIGRldGFpbGluZyBleGFjdGx5IGhvdyB0aGUgUEJNQzNrIGRhdGFzZXQgZmlndXJlcyBwcmVzZW50ZWQgaW4gb3VyIG1hbnVzY3JpcHQgd2VyZSBnZW5lcmF0ZWQuIFdlJ2xsIHN0YXJ0IGZyb20gYSAxMFggY291bnRzIG1hdHJpeCBhbmQgZW5kIHdpdGggZnVsbHkgYW5ub3RhdGVkIGNlbGwgY2x1c3RlcnMuIEluIGFkZGl0aW9uIHRvIFIsIGluIG9yZGVyIHRvIHJ1biBhbGwgdGhlIGNvZGUgaW4gdGhpcyBkb2N1bWVudCB5b3UnbGwgbmVlZCBhIFB5dGhvbiAzIGluc3RhbGxhdGlvbiB3aXRoIGBvcGVuVFNORWAgYW5kIGFsbCBpdHMgZGVwZW5kZW5jaWVzIGluc3RhbGxlZC4KCiMgTGlicmFyaWVzCgojIyBSCgpgYGB7cn0KbGlicmFyeShkcGx5cikgICAgICAgIyB0aWR5IGRhdGEgCmxpYnJhcnkoU2V1cmF0KSAgICAgICMgc2luZ2xlIGNlbGwgaW5mcmFzdHJ1Y3R1cmUKbGlicmFyeShnZ3Bsb3QyKSAgICAgIyBwbG90cwpsaWJyYXJ5KFNDSVNTT1JTKSAgICAjIHJlY2x1c3RlcmluZyAgCmxpYnJhcnkoZ2dzaWduaWYpICAgICMgc2lnbmlmaWNhbmNlIGJhcnMKbGlicmFyeShwYWxldHRlZXIpICAgIyBhZHZhbmNlZCBjb2xvcnMKbGlicmFyeShsYXRleDJleHApICAgIyBMYVRlWApsaWJyYXJ5KHJldGljdWxhdGUpICAjIFB5dGhvbiBpbnRlcmZhY2UKbGlicmFyeShnZ2JlZXN3YXJtKSAgIyBiZWVzd2FybSBwbG90cwpsaWJyYXJ5KFNldXJhdERhdGEpICAjIFBCTUMzayBkYXRhc2V0CmBgYAoKIyMgUHl0aG9uCgpgYGB7cHl0aG9ufQppbXBvcnQgbnVtcHkgYXMgbnAKZnJvbSBvcGVuVFNORSBpbXBvcnQgVFNORUVtYmVkZGluZwpmcm9tIG9wZW5UU05FIGltcG9ydCBpbml0aWFsaXphdGlvbgpmcm9tIG9wZW5UU05FLmFmZmluaXR5IGltcG9ydCBNdWx0aXNjYWxlCmZyb20gb3BlblRTTkUuYWZmaW5pdHkgaW1wb3J0IFBlcnBsZXhpdHlCYXNlZE5OCmBgYAoKIyBEYXRhCgpXZSBsb2FkIGEgc2NSTkEtc2VxIGRhdGFzZXQgcHJvdmlkZWQgYnkgMTBYIEdlbm9taWNzIHRoYXQgY29uc2lzdHMgb2YgMiw3MDAgcGVyaXBoZXJhbCBibG9vZCBtb25vbnVjbGVhciBjZWxscyAoUEJNQ3MpIGZyb20gYSBoZWFsdGh5IGRvbm9yLiBXZSByZW1vdmUgYWxsIGNlbGxzIHdpdGggdW5pcXVlIGZlYXR1cmUgY291bnRzIGdyZWF0ZXIgdGhhbiAyLDUwMCBpbiBvcmRlciB0byBwcmV2ZW50IGRvdWJsZXRzL211bHRpcGxldHMgZnJvbSBiZWluZyBpbmNsdWRlZCBpbiB0aGUgZGF0YXNldC4gCgpgYGB7cn0KcGJtYyA8LSBMb2FkRGF0YSgicGJtYzNrIikKcGJtYyA8LSBzdWJzZXQocGJtYywgc3Vic2V0ID0gbkZlYXR1cmVfUk5BIDwgMjUwMCkKYGBgCgojIFByZXByb2Nlc3NpbmcKCkhlcmUgd2UgdXNlIGBQcmVwYXJlRGF0YSgpYCB0byBub3JtYWxpemUgZXhwcmVzc2lvbiAmIHNlbGVjdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMgdGhyb3VnaCBgc2N0cmFuc2Zvcm1gLCBydW4gUENBICYgdC1TTkUuIGFuZCBjbHVzdGVyIG91ciBjZWxscy4gV2UgdXRpbGl6ZSAxNSBwcmluY2lwYWwgY29tcG9uZW50cyBldmVuIHRob3VnaCBbdGhlIFNhdGlqYSBMYWIgdmlnbmV0dGVdKChodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L3YzLjIvcGJtYzNrX3R1dG9yaWFsLmh0bWwpKSB1c2VkIDEwLCBhcyB3ZSdyZSB1c2luZyBgc2N0cmFuc2Zvcm1gIG5vcm1hbGl6YXRpb24sIHdoaWNoIGRvZXMgYSBiZXR0ZXIgam9iIG9mIHJldGFpbmluZyBiaW9sb2dpY2FsIGhldGVyb2dlbmVpdHkgdGhyb3VnaCBub3JtYWxpemF0aW9uIHRoYW4gc3RhbmRhcmQgbG9nLW5vcm1hbGl6YXRpb24uCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcGJtYyA8LSBQcmVwYXJlRGF0YShwYm1jLCAKICAgICAgICAgICAgICAgICAgICBuLkhWRyA9IDQwMDAsIAogICAgICAgICAgICAgICAgICAgIHJlZ3Jlc3MuY2MgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgcmVncmVzcy5tdCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICBuLlBDID0gMTUsIAogICAgICAgICAgICAgICAgICAgIHdoaWNoLmRpbS5yZWR1YyA9ICJ0c25lIiwKICAgICAgICAgICAgICAgICAgICBpbml0aWFsLnJlc29sdXRpb24gPSAuNCwgCiAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSA2MjkpCmBgYAoKV2Ugc2VlIGNsZWFyIHZpc3VhbCBldmlkZW5jZSBvZiBzdWJjbHVzdGVycy4gCgpgYGB7cn0KcDBhIDwtIERpbVBsb3QocGJtYywgcHQuc2l6ZSA9IDEuNzUsIGNvbHMgPSBwYWxldHRlZXJfZCgiZ2d0aGVtZXM6OkNsYXNzaWNfMTAiKSkgKyAKICAgICAgIGxhYnMoeCA9ICJ0LVNORSAxIiwgeSA9ICJ0LVNORSAyIikgKyAKICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDEsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkKcDBhCmBgYAoKV2UgY29tcHV0ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBzaWxob3VldHRlIHNjb3JlcyBmb3IgZWFjaCBjbHVzdGVyLiAKCmBgYHtyfQpzaWxfZGYgPC0gQ29tcHV0ZVNpbGhvdWV0dGVTY29yZXMocGJtYywgYXZnID0gRkFMU0UpCnAwYiA8LSBnZ3Bsb3Qoc2lsX2RmLCBhZXMoeCA9IENsdXN0ZXIsIHkgPSBTY29yZSwgZmlsbCA9IENsdXN0ZXIpKSArIAogICAgICAgZ2VvbV92aW9saW4oZHJhd19xdWFudGlsZXMgPSAwLjUsIGNvbG9yID0gImJsYWNrIiwgc2NhbGUgPSAid2lkdGgiKSArIAogICAgICAgc2NhbGVfZmlsbF9wYWxldHRlZXJfZCgiZ2d0aGVtZXM6OkNsYXNzaWNfMTAiKSArIAogICAgICAgbGFicyh5ID0gIlNpbGhvdWV0dGUgU2NvcmUiLCB4ID0gIkxvdXZhaW4gQ2x1c3RlcnMiLCBmaWxsID0gTlVMTCkgKyAKICAgICAgIHRoZW1lX21pbmltYWwoKSArIAogICAgICAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBzaXplID0gMSksIAogICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCAKICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoKSwgCiAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI0KSwgCiAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI0KSwgCiAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLAogICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkKYGBgCgpDbHVzdGVycyAwLCAxLCBhbmQgMiBoYXZlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnRseSBsb3dlciBzaWxob3VldHRlIHNjb3JlcyB0aGFuIHRoZSBvdGhlciB0aHJlZSBjbHVzdGVycy4gV2UgYWxzbyBzZWUgYSBzbWFsbCBjbHVtcCBvZiBjZWxscyBpbiBDbHVzdGVyIDQgdGhhdCBoYXMgbG93ZXIgc2NvcmVzIHRoYW4gdGhlIHJlc3Qgb2YgdGhlIGNsdXN0ZXIuIFdlJ2xsIGZvY3VzIG9uIHRoZXNlIGZvdXIgY2x1c3RlcnMgd2hlbiByZWNsdXN0ZXJpbmcsIGFzIGNsdXN0ZXJzIDMgYW5kIDUgc2VlbSBtb3N0bHkgY29ycmVjdC4gCgpgYGB7cn0KcDBiCmBgYAoKIyMgRml0LVNORQoKV2UnbGwgYWxzbyBydW4gdGhlIEZhc3QgRm91cmllciBUcmFuc2Zvcm0tYWNjZWxlcmF0ZWQgdmVyc2lvbiBvZiB0LVNORSBhcyBpbXBsZW1lbnRlZCBpbiB0aGUgUHl0aG9uIGxpYnJhcnkgYG9wZW5UU05FYC4gRmlyc3Qgd2UnbGwgbmVlZCB0byBnZXQgb3VyIFBDIG1hdHJpeCBpbnRvIGEgZm9ybSBhY2Nlc3NpYmxlIGJ5IG91ciBQeXRob24gaW50ZXJwcmV0ZXIuCgpgYGB7cn0KcGNfbWF0IDwtIEVtYmVkZGluZ3MocGJtYywgcmVkdWN0aW9uID0gInBjYSIpCmBgYAoKYGBge3B5dGhvbn0KIyBpbXBvcnQgUEMgbWF0cml4CnBjX21hdCA9IG5wLmFycmF5KHIucGNfbWF0KQojIHJ1biBGaXQtU05FIHcvIG11bHRpc2NhbGUga2VybmVsCmluaXQgPSBpbml0aWFsaXphdGlvbi5wY2EocGNfbWF0LCByYW5kb21fc3RhdGU9NjI5KQphZmZpbl9hbm5lYWwgPSBQZXJwbGV4aXR5QmFzZWROTihwY19tYXQsIHBlcnBsZXhpdHk9NTAsIG1ldHJpYz0nY29zaW5lJywgcmFuZG9tX3N0YXRlPTYyOSkKdHNuZSA9IFRTTkVFbWJlZGRpbmcoaW5pdCwgYWZmaW5fYW5uZWFsLCBuZWdhdGl2ZV9ncmFkaWVudF9tZXRob2Q9J2ZmdCcpCmVtYmVkMSA9IHRzbmUub3B0aW1pemUobl9pdGVyPTI1MCwgZXhhZ2dlcmF0aW9uPTEwLCBtb21lbnR1bT0wLjYpCmVtYmVkMiA9IGVtYmVkMS5vcHRpbWl6ZShuX2l0ZXI9NzUwLCBleGFnZ2VyYXRpb249MSwgbW9tZW50dW09MC44KQphZmZpbl9hbm5lYWwuc2V0X3BlcnBsZXhpdHkoMjApCmVtYmVkMyA9IGVtYmVkMi5vcHRpbWl6ZShuX2l0ZXI9NTAsIGV4YWdnZXJhdGlvbj0xLCBtb21lbnR1bT0wLjgpCmVtYmVkNCA9IGVtYmVkMy5vcHRpbWl6ZShuX2l0ZXI9MzUwKQpgYGAKCk5vdyB3ZSBwdWxsIHRoZSByZXN1bHRzIGJhY2sgaW50byBSLCBhbmQgc2F2ZSB0aGVtIGluIG91ciBgU2V1cmF0YCBvYmplY3QuIFdlIHNhdmUgdGhlIG9yaWdpbmFsIGVtYmVkZGluZyAobWFkZSB1c2luZyB0aGUgZGVmYXVsdCBCYXJuZXMtSHV0IGFwcHJveGltYXRlIHQtU05FIGltcGxlbWVudGF0aW9uKSBpbiBgcGJtY0ByZWR1Y3Rpb24kYmhfdHNuZWAgLSB3ZSBuZWVkIHRvIGRvIHRoaXMgaW4gb3JkZXIgdG8gbWFrZSB0aGUgRml0LVNORSBlbWJlZGRpbmcgdGhlIGRlZmF1bHQgZW1iZWRkaW5nIHRoYXQgd2lsbCBiZSByZXRyaWV2ZWQgaW4gY2FsbHMgdG8gZnVuY3Rpb25zIHN1Y2ggYXMgYERpbVBsb3QoKWAgb3IgYEZlYXR1cmVQbG90KClgLiAKCmBgYHtyfQplbWJlZCA8LSBhcy5tYXRyaXgocHkkZW1iZWQ0KQpyb3duYW1lcyhlbWJlZCkgPC0gY29sbmFtZXMocGJtYykKY29sbmFtZXMoZW1iZWQpIDwtIGMoIkZpdC1TTkVfMSIsICJGaXQtU05FXzIiKQpwYm1jQHJlZHVjdGlvbnMkYmhfdHNuZSA8LSBwYm1jQHJlZHVjdGlvbnMkdHNuZQpwYm1jQHJlZHVjdGlvbnMkdHNuZSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gZW1iZWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXkgPSAiRml0U05FXyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJTQ1QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2xvYmFsID0gVFJVRSkKYGBgCgpWaXN1YWxpemluZyB0aGUgcmVzdWx0cyBzaG93cyBwcmV0dHkgbXVjaCB0aGUgc2FtZSBnbG9iYWwgc3RydWN0dXJlIGFzIHdpdGggdGhlIGRlZmF1bHQgdC1TTkUgaW1wbGVtZW50YXRpb24sIGFsYmVpdCByb3RhdGVkIGEgYml0LCBidXQgSSBsaWtlIHRoYXQgRml0LVNORSdzIGNsdXN0ZXJzIGFyZSBhIGJpdCBkZW5zZXIsIHNvIHdlJ2xsIHVzZSBGaXQtU05FIGdvaW5nIGZvcndhcmQuCgpgYGB7cn0KcDEgPC0gRGltUGxvdChwYm1jLCBwdC5zaXplID0gMS43NSwgY29scyA9IHBhbGV0dGVlcl9kKCJnZ3RoZW1lczo6Q2xhc3NpY18xMCIpKSArIAogICAgICBsYWJzKHggPSAiRml0LVNORSAxIiwgeSA9ICJGaXQtU05FIDIiKSArIAogICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDEsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkKcDEKYGBgCgojIyBCcm9hZCBDZWxsIFR5cGVzCgpOZXh0IHdlJ2xsIHJ1biBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHRlc3QgaW4gb3JkZXIgdG8gYXNzaWduIGdlbmVyYWwgbGFiZWxzIHRvIG91ciBjbHVzdGVycy4gVGhlc2UgaWRlbnRpdGllcyB3aWxsIGhlbHAgZ3VpZGUgb3VyIHJlY2x1c3RlcmluZy4gCgpgYGB7cn0KcGJtY19kZSA8LSBGaW5kQWxsTWFya2VycyhwYm1jLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdC51c2UgPSAid2lsY294IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDYyOSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQpwYm1jX2RlICU+JSAKICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lIAogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSAKICB0b3BfbihuID0gNSwgd3QgPSBhdmdfbG9nMkZDKSAtPiB0b3A1X2RlCmBgYAoKRnJvbSB0aGUgdG9wIDUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGZvciBlYWNoIGNsdXN0ZXIsIHdlIGNhbiBkZWR1Y2Ugc29tZSBiYXNpYyBjbHVzdGVyIGlkZW50aXRpZXMuIENsdXN0ZXJzIDAgJiAyIGxvb2sgbGlrZSBUIGNlbGxzLiBDbHVzdGVycyAxICYgNCBhcHBlYXIgdG8gYmUgbW9ub2N5dGVzLiBDbHVzdGVyIDMgc3Ryb25nbHkgZXhwcmVzc2VzIEIgY2VsbCBtYXJrZXJzLiBMYXN0bHksIENsdXN0ZXIgNSBpcyBwcmV0dHkgY2xlYXJseSBpZGVudGlmaWFibGUgYXMgYmVpbmcgY29tcG9zZWQgb2YgTksgY2VsbHMuIFdoZW4gd2UgcmVjbHVzdGVyIG91ciBjZWxscywgd2UnbGwga2VlcCB0aGUgYmlvbG9naWNhbCBjb250ZXh0IG9mIHRoZXNlIGJyb2FkIGNlbGwgdHlwZXMgaW4gbWluZC4gCgpgYGB7cn0KcDIgPC0gRG90UGxvdChwYm1jLCBmZWF0dXJlcyA9IHVuaXF1ZSh0b3A1X2RlJGdlbmUpLCBkb3Quc2NhbGUgPSAxNSkgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHBhbGV0dGVlcl9kKCJ3ZXNhbmRlcnNvbjo6Wmlzc291MSIpKSArCiAgICAgIGxhYnMoY29sb3IgPSAiRXhwcmVzc2lvbiIsIHNpemUgPSAiJSBFeHByZXNzZWQiLCB5ID0gIkNsdXN0ZXIiKSArIAogICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTYsIHZqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIAogICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLCAKICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgc2l6ZSA9IDEsIGNvbG9yID0gImJsYWNrIiksIAogICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwgCiAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLCAKICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfY29sb3JiYXIodGl0bGUucG9zaXRpb24gPSAidG9wIiwgYmFyaGVpZ2h0ID0gdW5pdCgzLCB1bml0cyA9ICJjbSIpLCB0aXRsZS5oanVzdCA9IDAuNSksIAogICAgICAgICAgICAgc2l6ZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZS5wb3NpdGlvbiA9ICJ0b3AiLCB0aXRsZS5oanVzdCA9IDAuNSkpCnAyCmBgYAoKIyBSZWNsdXN0ZXJpbmcKCldlJ2xsIHJ1biBTQ0lTU09SUyBvbiB0aGUgbW9ub2N5dGVzICYgVCBjZWxsczsgaS5lLiBvbiBjbHVzdGVycyAxICYgNCBhbmQgMCAmIDIuIFdlIHVzZSBzbGlnaHRseSBkaWZmZXJlbnQgcG9zc2libGUgcGFyYW1ldGVyIHNldHMgYXMgdGhlIFQgY2VsbCBvYmplY3QgaXMgbXVjaCBsYXJnZXIgdGhhbiB0aGUgbW9ub2N5dGUgb2JqZWN0LiAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdob2xkJ30KbW9ub19yZWNsdXN0IDwtIFJlY2x1c3RlckNlbGxzKHBibWMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2guY2x1c3QgPSBsaXN0KDEsIDQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lcmdlLmNsdXN0ZXJzID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLkhWRyA9IDQwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLlBDID0gMTUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgay52YWxzID0gYygxMCwgMjAsIDMwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uLnZhbHMgPSBjKC4zLCAuNCwgLjUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZG8uZW1iZWRkaW5nID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDYyOSkKdF9yZWNsdXN0IDwtIFJlY2x1c3RlckNlbGxzKHBibWMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2guY2x1c3QgPSBsaXN0KDAsIDIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lcmdlLmNsdXN0ZXJzID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLkhWRyA9IDQwMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5QQyA9IDE1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGsudmFscyA9IGMoMzAsIDQwLCA1MCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbi52YWxzID0gYyguMywgLjQsIC41KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWRvLmVtYmVkZGluZyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSA2MjkpCmBgYAoKTm93IHdlJ2xsIHJ1biBGaXQtU05FIG9uIGVhY2ggb2YgdGhlIHJlY2x1c3RlcmVkIG9iamVjdHMgZm9yIGNvbnNpc3RlbmNpZXMgc2FrZS4gRmlyc3Qgd2UnbGwgbmVlZCB0byBjcmVhdGUgdmFyaWFibGVzIGNvbnRhaW5pbmcgdGhlIFBDIG1hdHJpY2VzIGFuZCBzZW5kIHRoZW0gdG8gUHl0aG9uLiAKCmBgYHtyfQpwY19tb25vIDwtIEVtYmVkZGluZ3MobW9ub19yZWNsdXN0LCByZWR1Y3Rpb24gPSAicGNhIikKcGNfdCA8LSBFbWJlZGRpbmdzKHRfcmVjbHVzdCwgcmVkdWN0aW9uID0gInBjYSIpCmBgYAoKV2UgcnVuIEZpdC1TTkUuIAoKYGBge3B5dGhvbn0KIyBpbXBvcnQgUEMgbWF0cmljZXMKcGNfbW9ubyA9IG5wLmFycmF5KHIucGNfbW9ubykKcGNfdCA9IG5wLmFycmF5KHIucGNfdCkKCiMgcnVuIEZpdC1TTkUgLSBNb25vY3l0ZXMKaW5pdF9tb25vID0gaW5pdGlhbGl6YXRpb24ucGNhKHBjX21vbm8sIHJhbmRvbV9zdGF0ZT02MjkpCmFmZmluX2FubmVhbF9tb25vID0gUGVycGxleGl0eUJhc2VkTk4ocGNfbW9ubywgcGVycGxleGl0eT01MCwgbWV0cmljPSdjb3NpbmUnLCByYW5kb21fc3RhdGU9NjI5KQp0c25lX21vbm8gPSBUU05FRW1iZWRkaW5nKGluaXRfbW9ubywgYWZmaW5fYW5uZWFsX21vbm8sIG5lZ2F0aXZlX2dyYWRpZW50X21ldGhvZD0nZmZ0JykKZW1iZWQxX21vbm8gPSB0c25lX21vbm8ub3B0aW1pemUobl9pdGVyPTI1MCwgZXhhZ2dlcmF0aW9uPTEyLCBtb21lbnR1bT0wLjYpCmVtYmVkMl9tb25vID0gZW1iZWQxX21vbm8ub3B0aW1pemUobl9pdGVyPTk1MCwgZXhhZ2dlcmF0aW9uPTEsIG1vbWVudHVtPTAuOCkKCiMgcnVuIEZpdC1TTkUgLSBUIGNlbGxzCmluaXRfdCA9IGluaXRpYWxpemF0aW9uLnBjYShwY190LCByYW5kb21fc3RhdGU9NjI5KQphZmZpbl9hbm5lYWxfdCA9IFBlcnBsZXhpdHlCYXNlZE5OKHBjX3QsIHBlcnBsZXhpdHk9NTAsIG1ldHJpYz0nY29zaW5lJywgcmFuZG9tX3N0YXRlPTYyOSkKdHNuZV90ID0gVFNORUVtYmVkZGluZyhpbml0X3QsIGFmZmluX2FubmVhbF90LCBuZWdhdGl2ZV9ncmFkaWVudF9tZXRob2Q9J2ZmdCcpCmVtYmVkMV90ID0gdHNuZV90Lm9wdGltaXplKG5faXRlcj0yNTAsIGV4YWdnZXJhdGlvbj0xMiwgbW9tZW50dW09MC42KQplbWJlZDJfdCA9IGVtYmVkMV90Lm9wdGltaXplKG5faXRlcj05NTAsIGV4YWdnZXJhdGlvbj0xLCBtb21lbnR1bT0wLjgpCmBgYAoKTm93IHdlIGJyaW5nIHRoZSByZXN1bHRzIGJhY2sgaW4gdG8gUi4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQplbWJlZF9tb25vIDwtIGFzLm1hdHJpeChweSRlbWJlZDJfbW9ubykKcm93bmFtZXMoZW1iZWRfbW9ubykgPC0gY29sbmFtZXMobW9ub19yZWNsdXN0KQptb25vX3JlY2x1c3RAcmVkdWN0aW9ucyRiaF90c25lIDwtIG1vbm9fcmVjbHVzdEByZWR1Y3Rpb25zJHRzbmUKbW9ub19yZWNsdXN0QHJlZHVjdGlvbnMkdHNuZSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gZW1iZWRfbW9ubywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5ID0gIkZpdFNORV8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlNDVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdsb2JhbCA9IFRSVUUpCmVtYmVkX3QgPC0gYXMubWF0cml4KHB5JGVtYmVkMl90KQpyb3duYW1lcyhlbWJlZF90KSA8LSBjb2xuYW1lcyh0X3JlY2x1c3QpCnRfcmVjbHVzdEByZWR1Y3Rpb25zJGJoX3RzbmUgPC0gdF9yZWNsdXN0QHJlZHVjdGlvbnMkdHNuZQp0X3JlY2x1c3RAcmVkdWN0aW9ucyR0c25lIDwtIENyZWF0ZURpbVJlZHVjT2JqZWN0KGVtYmVkZGluZ3MgPSBlbWJlZF90LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleSA9ICJGaXRTTkVfIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJTQ1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdsb2JhbCA9IFRSVUUpCmBgYAoKSGVyZSdzIHdoYXQgb3VyIHJlY2x1c3RlcmluZ3MgbG9vayBsaWtlLiBUaGVyZSdzIGNsZWFyIHZpc3VhbCBzZXBhcmF0aW9uIGJldHdlZW4gdGhlIG1haW4gY2x1c3RlcnMgYW5kIHRoZSBzdWJncm91cHMgd2UndmUgZGlzY292ZXJlZCB1c2luZyBgU0NJU1NPUlNgLgoKYGBge3J9CnAzIDwtIERpbVBsb3QobW9ub19yZWNsdXN0LCBwdC5zaXplID0gMS43NSwgY29scyA9IHBhbGV0dGVlcl9kKCJnZ3NjaTo6ZGVmYXVsdF9uZWptIikpICsgCiAgICAgIGxhYnMoeCA9ICJGaXQtU05FIDEiLCB5ID0gIkZpdC1TTkUgMiIpICsgCiAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKQpwNCA8LSBEaW1QbG90KHRfcmVjbHVzdCwgcHQuc2l6ZSA9IDEuNzUsIGNvbHMgPSBwYWxldHRlZXJfZCgiZ2dzY2k6OmRlZmF1bHRfbmVqbSIpKSArIAogICAgICBsYWJzKHggPSAiRml0LVNORSAxIiwgeSA9ICJGaXQtU05FIDIiKSArIAogICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDEsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkKcDMKcDQKYGBgCgpOZXh0LCB3ZSdsbCByZWludGVncmF0ZSBvdXIgbmV3IGNsdXN0ZXJzIGludG8gb3VyIG9yaWdpbmFsIGBTZXVyYXRgIG9iamVjdCAtIHRoaXMgcmVxdWlyZXMgc29tZSBmaW5hZ2xpbmcgYXMgYFNldXJhdGAgaXMgYSBiaXQgd2VpcmQgd2l0aCBob3cgaXQgc3RvcmVzIGNlbGwtbGV2ZWwgbWV0YWRhdGEuIFNpbmNlIHdlIGhhZCBzaXggY2x1c3RlcnMgb3JpZ2luYWxseSwgYW5kIHdlIGRpc2NvdmVyZWQgc2l4IG5ldyBzdWJjbHVzdGVycywgd2UnbGwgZW5kIHVwIHdpdGggdHdlbHZlIHRvdGFsIGNsdXN0ZXJzLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnBibWMgPC0gSW50ZWdyYXRlU3ViY2x1c3RlcnMob3JpZ2luYWwub2JqZWN0ID0gcGJtYywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjbHVzdC5yZXN1bHRzID0gbGlzdChtb25vX3JlY2x1c3QsIHRfcmVjbHVzdCkpCnA1IDwtIERpbVBsb3QocGJtYywgcHQuc2l6ZSA9IDEuNzUsIGNvbHMgPSBwYWxldHRlZXJfZCgiZ2d0aGVtZXM6OkNsYXNzaWNfMTAiKSkgKyAKICAgICAgbGFicyh4ID0gIkZpdC1TTkUgMSIsIHkgPSAiRml0LVNORSAyIikgKyAKICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAxLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpCnA1CmBgYAoKIyBJZGVudGlmeSBDZWxsIFR5cGVzCgpOb3cgdGhhdCB3ZSd2ZSBkZXRlcm1pbmVkIG91ciBzdWJwb3B1bGF0aW9ucywgd2UgY2FuIGFzc2lnbiBjZWxsIHR5cGVzIHRvIGVhY2ggY2x1c3RlciB1c2luZyB0aGUgbWFya2VyIGdlbmVzIHByb3ZpZGVkIGluIFt0aGUgU2F0aWphIExhYidzIFBCTUMzayB2aWduZXR0ZV0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC92My4yL3BibWMza190dXRvcmlhbC5odG1sKSwgYXMgd2VsbCBhcyBvdGhlciBjYW5vbmljYWwgbWFya2Vycy4KCiMjIENENCsgVCBDZWxscwoKV2UgY2FuIHF1aWNrbHkgaWRlbnRpZnkgY2x1c3RlciA3IGFzIHRoZSBuYWl2ZSBDRDQrIFQgY2VsbHMsIGFuZCBjbHVzdGVyIDggYXMgdGhlIG1lbW9yeSBDRDQrIHBvcHVsYXRpb24uCgpgYGB7cn0KcDYgPC0gRmVhdHVyZVBsb3QocGJtYywgcHQuc2l6ZSA9IDEuNzUsIGZlYXR1cmVzID0gIklMN1IiKSArIAogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgIE5vTGVnZW5kKCkgKyAKICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKcDcgPC0gRmVhdHVyZVBsb3QocGJtYywgcHQuc2l6ZSA9IDEuNzUsIGZlYXR1cmVzID0gIkNDUjciKSArIAogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgIHRoZW1lX3llaGxhYigpICArIAogICAgICBOb0xlZ2VuZCgpICsgCiAgICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCnA4IDwtIEZlYXR1cmVQbG90KHBibWMsIHB0LnNpemUgPSAxLjc1LCBmZWF0dXJlcyA9ICJTMTAwQTQiKSArIAogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgIE5vTGVnZW5kKCkgKyAKICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKKHA2IHwgcDcgfCBwOCkgLyBwNQpgYGAKCiMjIENEMTQrIE1vbm9jeXRlcwoKQ2x1c3RlciAyIGNsZWFybHkgaG91c2VzIG91ciBDRDE0KyBtb25vY3l0ZXMuCgpgYGB7cn0KcDkgPC0gRmVhdHVyZVBsb3QocGJtYywgcHQuc2l6ZSA9IDEuNzUsIGZlYXR1cmVzID0gIkNEMTQiKSArIAogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgIE5vTGVnZW5kKCkgKyAKICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKcDEwIDwtIEZlYXR1cmVQbG90KHBibWMsIHB0LnNpemUgPSAxLjc1LCBmZWF0dXJlcyA9ICJMWVoiKSArIAogICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHBhbGV0dGVlcl9kKCJ3ZXNhbmRlcnNvbjo6Wmlzc291MSIpKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIE5vTGVnZW5kKCkgKyAKICAgICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCihwOSB8IHAxMCkgLyBwNQpgYGAKCiMjIEZDR1IzQSsgTW9ub2N5dGVzCgpUaGUgRkNHUjNBKyBtb25vY3l0ZXMgYXJlIHBvc2l0aW9uZWQgbmVhciB0aGUgQ0QxNCsgbW9ub2N5dGVzIGluIGNsdXN0ZXIgNC4gTm90ZTogdGhlc2UgYXJlIGFsc28ga25vd24gYXMgQ0QxNisgbW9ub2N5dGVzLiAKCmBgYHtyfQpwMTEgPC0gRmVhdHVyZVBsb3QocGJtYywgcHQuc2l6ZSA9IDEuNzUsIGZlYXR1cmVzID0gIkZDR1IzQSIpICsgCiAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICAgTm9MZWdlbmQoKSArIAogICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKcDEyIDwtIEZlYXR1cmVQbG90KHBibWMsIHB0LnNpemUgPSAxLjc1LCBmZWF0dXJlcyA9ICJNUzRBNyIpICsgCiAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICAgTm9MZWdlbmQoKSArIAogICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKKHAxMSB8IHAxMikgLyBwNQpgYGAKCiMjIEludGVybWVkaWF0ZSBNb25vY3l0ZXMKCkxhc3RseSwgaXQgZm9sbG93cyB0aGF0IGJldHdlZW4gdGhlIENEMTQrIGFuZCBGQ0dSM0ErIG1vbm9jeXRlcyBhcmUgdGhlIGludGVybWVkaWF0ZSBtb25vY3l0ZXMsIHdoaWNoIFthcmUgY2hhcmFjdGVyaXplZCBieSBleHByZXNzaW9uIG9mIENEMTQgYW5kIENEMTYsIGFsb25nIHdpdGggUzEwMEE4LCBDRDc0LCBITEEtRFBCMSwgZXRjIF0oaHR0cHM6Ly93d3cuZnJvbnRpZXJzaW4ub3JnL2FydGljbGVzLzEwLjMzODkvZmltbXUuMjAxOS4wMjAzNS9mdWxsI0IzKS4gCgpgYGB7cn0KcDEzIDwtIEZlYXR1cmVQbG90KHBibWMsIHB0LnNpemUgPSAxLjc1LCBmZWF0dXJlcyA9ICJITEEtRFBCMSIpICsgCiAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICAgTm9MZWdlbmQoKSArIAogICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKcDE0IDwtIEZlYXR1cmVQbG90KHBibWMsIHB0LnNpemUgPSAxLjc1LCBmZWF0dXJlcyA9ICJDRDc0IikgKyAKICAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBwYWxldHRlZXJfZCgid2VzYW5kZXJzb246Olppc3NvdTEiKSkgKyAKICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICBOb0xlZ2VuZCgpICsgCiAgICAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpwMTUgPC0gRmVhdHVyZVBsb3QocGJtYywgcHQuc2l6ZSA9IDEuNzUsIGZlYXR1cmVzID0gIkhMQS1EUkEiKSArIAogICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHBhbGV0dGVlcl9kKCJ3ZXNhbmRlcnNvbjo6Wmlzc291MSIpKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIE5vTGVnZW5kKCkgKyAKICAgICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCihwMTMgfCBwMTQgfCBwMTUpIC8gcDUKYGBgCgojIyBCIENlbGxzCgpFeHByZXNzaW9uIG9mIE1TNEExIGFsbG93cyB1cyB0byBpc29sYXRlIHRoZSBCIGNlbGxzIGluIGNsdXN0ZXIgMC4KCmBgYHtyfQpwMTYgPC0gRmVhdHVyZVBsb3QocGJtYywgcHQuc2l6ZSA9IDEuNzUsIGZlYXR1cmVzID0gIk1TNEExIikgKyAKICAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBwYWxldHRlZXJfZCgid2VzYW5kZXJzb246Olppc3NvdTEiKSkgKyAKICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICBOb0xlZ2VuZCgpICsgCiAgICAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpwMTYgLyBwNQpgYGAKCiMjIENEOCsgVCBDZWxscwoKVGhlIGNhbm9uaWNhbCBtYXJrZXIgQ0Q4QSBzd2lmdGx5IGlkZW50aWZpZXMgb3VyIENEOCsgVCBjZWxscyBpbiBjbHVzdGVyIDkuCgpgYGB7cn0KcDE3IDwtIEZlYXR1cmVQbG90KHBibWMsIHB0LnNpemUgPSAxLjc1LCBmZWF0dXJlcyA9ICJDRDhBIikgKyAKICAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBwYWxldHRlZXJfZCgid2VzYW5kZXJzb246Olppc3NvdTEiKSkgKyAKICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICBOb0xlZ2VuZCgpICsgCiAgICAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpwMTcgLyBwNQpgYGAKCiMjIE5hdHVyYWwgS2lsbGVyIENlbGxzCgpXZSBjYW4gdXNlIE5LRzcgYW5kIEdOTFkgdG8gaXNvbGF0ZSB0aGUgTksgY2VsbHMgaW4gY2x1c3RlciAxLgoKYGBge3J9CnAxOCA8LSBGZWF0dXJlUGxvdChwYm1jLCBwdC5zaXplID0gMS43NSwgZmVhdHVyZXMgPSAiTktHNyIpICsgCiAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICAgTm9MZWdlbmQoKSArIAogICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKcDE5IDwtIEZlYXR1cmVQbG90KHBibWMsIHB0LnNpemUgPSAxLjc1LCBmZWF0dXJlcyA9ICJHTkxZIikgKyAKICAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBwYWxldHRlZXJfZCgid2VzYW5kZXJzb246Olppc3NvdTEiKSkgKyAKICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICBOb0xlZ2VuZCgpICsgCiAgICAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQoocDE4IHwgcDE5KSAvIHA1CmBgYAoKIyMgRGVuZHJpdGljIENlbGxzCgpUaGUgZGVuZHJpdGljIGNlbGwgZ3JvdXAgaXMgZGVmaW5lZCBieSBleHByZXNzaW9uIG9mIEZDRVIxQSBhbmQgQ1NUMyBpbiBjbHVzdGVyIDUuCgpgYGB7cn0KcDIwIDwtIEZlYXR1cmVQbG90KHBibWMsIHB0LnNpemUgPSAxLjc1LCBmZWF0dXJlcyA9ICJGQ0VSMUEiKSArIAogICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHBhbGV0dGVlcl9kKCJ3ZXNhbmRlcnNvbjo6Wmlzc291MSIpKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIE5vTGVnZW5kKCkgKyAKICAgICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCnAyMSA8LSBGZWF0dXJlUGxvdChwYm1jLCBwdC5zaXplID0gMS43NSwgZmVhdHVyZXMgPSAiQ1NUMyIpICsgCiAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICAgTm9MZWdlbmQoKSArIAogICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKKHAyMCB8IHAyMSkgLyBwNQpgYGAKCiMjIFBsYXRlbGV0cwoKVGhlIHRpbnkgcGxhdGVsZXQgcG9wdWxhdGlvbiBvZiBgciBzdW0ocGJtYyRzZXVyYXRfY2x1c3RlcnMgPT0gNilgIGNlbGxzIGNhbiBiZSBpZGVudGlmaWVkIGJ5IGl0cyBleHByZXNzaW9uIG9mIFBQQlAgaW4gY2x1c3RlciA2LgoKYGBge3J9CnAyMiA8LSBGZWF0dXJlUGxvdChwYm1jLCBwdC5zaXplID0gMS43NSwgZmVhdHVyZXMgPSAiUFBCUCIpICsgCiAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcGFsZXR0ZWVyX2QoIndlc2FuZGVyc29uOjpaaXNzb3UxIikpICsgCiAgICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICAgTm9MZWdlbmQoKSArIAogICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKcDIyIC8gcDUKYGBgCgojIyBGaW5hbCBGaWd1cmUKCkZpbmFsbHksIHdlJ2xsIGFkZCBjZWxsIGxhYmVscyB0byBvdXIgb3JpZ2luYWwgYFNldXJhdGAgb2JqZWN0IGFuZCBwbG90IHRoZSByZXN1bHRzLgoKYGBge3J9CnBibWMkbGFiZWwgPC0gY2FzZV93aGVuKHBibWMkc2V1cmF0X2NsdXN0ZXJzID09IDAgfiAiQiIsIAogICAgICAgICAgICAgICAgICAgICAgICBwYm1jJHNldXJhdF9jbHVzdGVycyA9PSAxIH4gIk5LIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHBibWMkc2V1cmF0X2NsdXN0ZXJzID09IDIgfiAiQ0QxNCsgTW9ub2N5dGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgcGJtYyRzZXVyYXRfY2x1c3RlcnMgPT0gMyB+ICJJbnRlcm1lZGlhdGUgTW9ub2N5dGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgcGJtYyRzZXVyYXRfY2x1c3RlcnMgPT0gNCB+ICJGQ0dSM0ErIE1vbm9jeXRlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHBibWMkc2V1cmF0X2NsdXN0ZXJzID09IDUgfiAiREMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgcGJtYyRzZXVyYXRfY2x1c3RlcnMgPT0gNiB+ICJQbGF0ZWxldCIsIAogICAgICAgICAgICAgICAgICAgICAgICBwYm1jJHNldXJhdF9jbHVzdGVycyA9PSA3IH4gIk5haXZlIENENCsgVCIsIAogICAgICAgICAgICAgICAgICAgICAgICBwYm1jJHNldXJhdF9jbHVzdGVycyA9PSA4IH4gIk1lbW9yeSBDRDQrIFQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgcGJtYyRzZXVyYXRfY2x1c3RlcnMgPT0gOSB+ICJDRDgrIFQiKQpwYm1jJGxhYmVsIDwtIGZhY3RvcihwYm1jJGxhYmVsLCBsZXZlbHMgPSBjKCJCIiwgIk5LIiwgIkNEMTQrIE1vbm9jeXRlIiwgIkludGVybWVkaWF0ZSBNb25vY3l0ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGQ0dSM0ErIE1vbm9jeXRlIiwgIkRDIiwgIlBsYXRlbGV0IiwgIk5haXZlIENENCsgVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZW1vcnkgQ0Q0KyBUIiwgIkNEOCsgVCIpKQpJZGVudHMocGJtYykgPC0gImxhYmVsIgpgYGAKCldlIHZpc3VhbGl6ZSB0aGUgZmluYWwgY2VsbHMgbGFiZWxzIGZvciBvdXIgMTEgY2x1c3RlcnMuICAKCmBgYHtyfQpwMjMgPC0gRGltUGxvdChwYm1jLCBwdC5zaXplID0gMS43NSwgY29scyA9IHBhbGV0dGVlcl9kKCJnZ3RoZW1lczo6Q2xhc3NpY18xMCIpKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMywgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKSArIAogICAgICAgbGFicyh4ID0gIkZpdC1TTkUgMSIsIHkgPSAiRml0LVNORSAyIiwgdGl0bGUgPSBOVUxMKQpwMjMKYGBgCgpXZSBwZXJmb3JtIGEgZmluYWwgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgZm9yIG91ciBjZWxsIHR5cGVzLiAKCmBgYHtyfQpmaW5hbF9kZSA8LSBGaW5kQWxsTWFya2VycyhwYm1jLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LnVzZSA9ICJ3aWxjb3giLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSA2MjkpCmZpbmFsX2RlICU+JSAKICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lIAogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSAKICB0b3BfbihuID0gNSwgd3QgPSBhdmdfbG9nMkZDKSAtPiB0b3BfZGVfZmluYWwKcDI0IDwtIERvdFBsb3QocGJtYywgZmVhdHVyZXMgPSB1bmlxdWUodG9wX2RlX2ZpbmFsJGdlbmUpLCBkb3Quc2NhbGUgPSAxNSkgKyAKICAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBwYWxldHRlZXJfZCgid2VzYW5kZXJzb246Olppc3NvdTEiKSkgKwogICAgICAgbGFicyhjb2xvciA9ICJFeHByZXNzaW9uIiwgc2l6ZSA9ICIlIEV4cHJlc3NlZCIsIHkgPSBOVUxMKSArIAogICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDE2LCB2anVzdCA9IDAuNSksIAogICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLCAKICAgICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIHNpemUgPSAxLCBjb2xvciA9ICJibGFjayIpLCAKICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwgCiAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIAogICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKyAKICAgICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2NvbG9yYmFyKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIGJhcmhlaWdodCA9IHVuaXQoMywgdW5pdHMgPSAiY20iKSwgdGl0bGUuaGp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgICBzaXplID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41KSkKYGBgCgpPdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cyBjb25maXJtIG91ciBtYW51YWwgYW5ub3RhdGlvbnMuIEVhY2ggb2YgdGhlIGNlbGx0eXBlcyBleHByZXNzZXMgc29tZSBvZiBpdHMgY2Fub25pY2FsIG1hcmtlcnMsIHNvIHdlIGNhbiBiZSBwcmV0dHkgY29uZmlkZW50IHRoYXQgd2UgZ290IHRoZSBhbm5vdGF0aW9ucyByaWdodC4gCgpgYGB7cn0KcDI0CmBgYAoKIyBDb25jbHVzaW9ucwoKU0NJU1NPUlMgYXV0b21hdGljYWxseSBzcGxpdCBhIGxhcmdlIENENCsgVCBjbHVzdGVyIGludG8gbmFpdmUgYW5kIG1lbW9yeSBDRDQrIFQgY2VsbHMuIEl0IHN1Y2Nlc3NmdWxseSBzZXBhcmF0ZWQgYSBzbWFsbCBkZW5kcml0aWMgY2VsbCBjbHVzdGVyIGZyb20gdGhlIENEMTQrIG1vbm9jeXRlcyBpdCBoYWQgb3JpZ2luYWxseSBiZWVuIGdyb3VwZWQgd2l0aCwgYW5kIHRlYXNlZCBvdXQgdGhlIGludGVybWVkaWF0ZSBtb25vY3l0ZSBwb3B1bGF0aW9uIG5lc3RsZWQgYmV0d2VlbiB0aGUgQ0QxNCsgYW5kIENEMTYrIG1vbm9jeXRlIGNsdXN0ZXJzLiBMYXN0bHksIGl0IGlkZW50aWZpZWQgYSB0aW55IHBsYXRlbGV0IGNsdXN0ZXIgdGhhdCBoYWQgYmVlbiBlcnJvbmVvdXNseSBncm91cGVkIHdpdGggdGhlIENEMTYrIG1vbm9jeXRlcy4gVGhlIGludGVybWVkaWF0ZSBtb25vY3l0ZXMgd2VyZSBub3QgYW5ub3RhdGVkIGluIFt0aGUgb3JpZ2luYWwgU2F0aWphIExhYiBQQk1DM2sgdmlnbmV0dGVdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvdjMuMi9wYm1jM2tfdHV0b3JpYWwuaHRtbCkuIAoKV2UgdXNlZCB0aGUgUEJNQzNrIGRhdGFzZXQgYmVjYXVzZSBvZiAxKSBpdHMgaW1tZWRpYXRlIGF2YWlsYWJpbGl0eSB0byBhbnlvbmUgd2lzaGluZyB0byByZXBsaWNhdGUgb3VyIHJlc3VsdHMgYW5kIDIpIHRoZSB2YWxpZGl0eSBvZiBpdHMgYW5ub3RhdGlvbnMsIHdoaWNoIGFsbG93ZWQgdXMgdG8gYmUgY29uZmlkZW50IGluIHRoZSByZXN1bHRzIGZyb20gU0NJU1NPUlMsIHdoaWNoIHdhcyBhYmxlIHRvIGNhcnZlIG91dCByYXJlIGNlbGwgZ3JvdXBzIGZyb20gbGFyZ2VyLCBicm9hZGVyIGNlbGwgdHlwZXMuIEluIHRoaXMgY2FzZSwgdGhlIGRlbmRyaXRpYyBjZWxsIGNsdXN0ZXIgd2FzIGNvbXBvc2VkIG9mIDM3IGNlbGxzLCBhbmQgdGhlIHBsYXRlbGV0IGNsdXN0ZXIgb2YganVzdCAxNCBjZWxscy4gSG93ZXZlciwgU0NJU1NPUlMgaXNuJ3QganVzdCB1c2VmdWwgZm9yIHJhcmUgY2VsbCB0eXBlczogaXQgYWxzbyBpZGVudGlmaWVkIHRoZSBpbnRlcm1lZGlhdGUgbW9ub2N5dGVzLCBhIGNsdXN0ZXIgb2YgMTkxIGNlbGxzLiBXZSB0aHVzIGJlbGlldmUgd2UgY2FuIGNvbmZpZGVudGx5IHNheSB0aGF0IFNDSVNTT1JTIGhhcyBiZWVuIHNob3duIHRvIGFjY3VyYXRlbHkgYW5kIHN3aWZ0bHkgaWRlbnRpZnkgY2VsbCB0eXBlcywgYm90aCBjb21tb24gYW5kIHJhcmUsIGJ5IGNvbnNpZGVyaW5nIHRoZSB2YXJpYW5jZSBpbiBnZW5lIGV4cHJlc3Npb24gd2l0aGluIGNsdXN0ZXJzIGFuZCBqdWRnaW5nIGl0ZXJhdGl2ZSByZWNsdXN0ZXJpbmcgdXNpbmcgc2lsaG91ZXR0ZSBzY29yZXMuIFdlIHB1dCBmb3J0aCB0aGF0IHRoaXMgYXBwcm9hY2ggaXMgdGhlb3JldGljYWxseSBhZHZhbnRhZ2VvdXMgZm9yIGlkZW50aWZ5aW5nIHJhcmUgY2VsbCBwb3B1bGF0aW9ucywgcmF0aGVyIHRoYW4gYXR0ZW1wdGluZyB0byBkbyBzbyBhdCB0aGUgbGV2ZWwgb2YgdGhlIGVudGlyZSBkYXRhc2V0LgoKIyBTYXZlIERhdGEgJiBGaWd1cmVzCgpUaGlzIHBhcnQgaXNuJ3QgcmVhbGx5IHdvcnRoIHJlYWRpbmc7IGl0J3MganVzdCBoZXJlIHRvIHByb3ZlIHRoYXQgYWxsIHRoZSBmaWd1cmVzIHdlcmUgYWN0dWFsbHkgZHluYW1pY2FsbHkgZ2VuZXJhdGVkIGFuZCBzYXZlZCB1cG9uIGtuaXR0aW5nIHRoaXMgZG9jdW1lbnQuCgpGaXJzdCB3ZSBzYXZlIHRoZSBmaW5hbGl6ZWQgYFNldXJhdGAgb2JqZWN0LiAKCmBgYHtyfQpzYXZlUkRTKHBibWMsIGZpbGUgPSAifi9EZXNrdG9wL0RhdGEvcGJtYzNrLlJkcyIpCmBgYAoKV2UnbGwgYWxzbyBzYXZlIHRoZSBmaW5hbCBERSBnZW5lcyBhcyBhbiBFeGNlbCBzcHJlYWRzaGVldC4gCgpgYGB7cn0Kb3Blbnhsc3g6OndyaXRlLnhsc3goZmluYWxfZGUsIGZpbGUgPSAifi9EZXNrdG9wL0RhdGEvUEJNQ19ERS54bHN4IikKYGBgCgpXZSdsbCBjcmVhdGUgYSBxdWljayBjb252ZW5pZW5jZSBmdW5jdGlvbiB0byBoZWxwIHVzIHNhdmUgdGhlIGZpZ3VyZXMuCgpgYGB7cn0KU2F2ZUZpZ3VyZSA8LSBmdW5jdGlvbihteS5wbG90ID0gTlVMTCwgbmFtZSA9IE5VTEwsIGhlaWdodCA9IDgsIHdpZHRoID0gOCkgewogIGlmIChpcy5udWxsKHBsb3QpIHwgaXMubnVsbChuYW1lKSkgc3RvcCgiWW91IGZvcmdvdCBzb21lIGFyZ3VtZW50cy4iKQogICMgc2F2ZSBmaWd1cmUgYXMgaXMgLSB3LyBheGlzIGxhYmVscywgdGl0bGVzLCBldGMuIAogIGRpciA8LSAifi9EZXNrdG9wL1IvU0NJU1NPUlMvdmlnbmV0dGVzL2ZpZ3VyZXNfc3VwcC9QQk1DIgogIGdnc2F2ZShteS5wbG90LCAKICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAobmFtZSwgIi5wZGYiKSwgCiAgICAgICAgIGRldmljZSA9ICJwZGYiLCAKICAgICAgICAgdW5pdHMgPSAiaW4iLAogICAgICAgICBwYXRoID0gZGlyLCAKICAgICAgICAgaGVpZ2h0ID0gaGVpZ2h0LCAKICAgICAgICAgd2lkdGggPSB3aWR0aCkgCiAgIyBzYXZlICJibGFuayIgZmlndXJlIHcvIG5vIGxhYmVscywgbGVnZW5kcywgZXRjLgogIGRpciA8LSAifi9EZXNrdG9wL1IvU0NJU1NPUlMvdmlnbmV0dGVzL2ZpZ3VyZXNfcHViL1BCTUMiCiAgcGxvdF9ibGFuayA8LSBteS5wbG90ICsgCiAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIGdnc2F2ZShwbG90X2JsYW5rLCAKICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAobmFtZSwgIi5wZGYiKSwgCiAgICAgICAgIGRldmljZSA9ICJwZGYiLCAKICAgICAgICAgdW5pdHMgPSAiaW4iLAogICAgICAgICBwYXRoID0gZGlyLCAKICAgICAgICAgaGVpZ2h0ID0gaGVpZ2h0LCAKICAgICAgICAgd2lkdGggPSB3aWR0aCkgCn0KYGBgCgpMYXN0bHksIHdlJ2xsIHNhdmUgdGhlIGZpZ3VyZXMgdW5kZXIgYC4vdmlnbmV0dGVzL2ZpZ3VyZXMvYC4gCgpgYGB7cn0KU2F2ZUZpZ3VyZShwMGEsIG5hbWUgPSAiU2V1cmF0X0NsdXN0ZXJzIikKU2F2ZUZpZ3VyZShwMGIsIG5hbWUgPSAiU2V1cmF0X0NsdXN0ZXJzX1NpbGhvdWV0dGVzIikKU2F2ZUZpZ3VyZShwMSwgbmFtZSA9ICJTZXVyYXRfQ2x1c3RlcnNfRml0U05FIikKU2F2ZUZpZ3VyZShwMiwgbmFtZSA9ICJNYXJrZXJfR2VuZXNfRm9yX1NldXJhdF9DbHVzdGVycyIsIGhlaWdodCA9IDgsIHdpZHRoID0gMTgpClNhdmVGaWd1cmUocDMsIG5hbWUgPSAiTW9ub2N5dGVfQ2x1c3Rlcl9TQ0lTU09SUyIpClNhdmVGaWd1cmUocDQsIG5hbWUgPSAiVF9DbHVzdGVyX1NDSVNTT1JTIikKU2F2ZUZpZ3VyZShwNSwgbmFtZSA9ICJTQ0lTU09SU19DbHVzdGVyc19GaW5hbCIpClNhdmVGaWd1cmUocDYsIG5hbWUgPSAiQ0Q0VF9JTDdSIikKU2F2ZUZpZ3VyZShwNywgbmFtZSA9ICJDRDRUX0NDUjciKQpTYXZlRmlndXJlKHA4LCBuYW1lID0gIkNENFRfUzEwMEE0IikKU2F2ZUZpZ3VyZShwOSwgbmFtZSA9ICJNb25vY3l0ZV9DRDE0IikKU2F2ZUZpZ3VyZShwMTAsIG5hbWUgPSAiTW9ub2N5dGVfTFlaIikKU2F2ZUZpZ3VyZShwMTEsIG5hbWUgPSAiRkNHUjNBX01vbm9jeXRlX0ZDR1IzQSIpClNhdmVGaWd1cmUocDEyLCBuYW1lID0gIkZDR1IzQV9Nb25vY3l0ZV9NUzRBNyIpClNhdmVGaWd1cmUocDEzLCBuYW1lID0gIkludGVybWVkaWF0ZV9Nb25vX0hMQURQQjEiKQpTYXZlRmlndXJlKHAxNCwgbmFtZSA9ICJJbnRlcm1lZGlhdGVfTW9ub19DRDc0IikKU2F2ZUZpZ3VyZShwMTUsIG5hbWUgPSAiSW50ZXJtZWRpYXRlX01vbm9fSExBRFJBIikKU2F2ZUZpZ3VyZShwMTYsIG5hbWUgPSAiQl9NUzRBMSIpClNhdmVGaWd1cmUocDE3LCBuYW1lID0gIkNEOFRfQ0Q4QSIpClNhdmVGaWd1cmUocDE4LCBuYW1lID0gIk5LX05LRzciKQpTYXZlRmlndXJlKHAxOSwgbmFtZSA9ICJOS19HTkxZIikKU2F2ZUZpZ3VyZShwMjAsIG5hbWUgPSAiRENfRkNFUjFBIikKU2F2ZUZpZ3VyZShwMjEsIG5hbWUgPSAiRENfQ1NUMyIpClNhdmVGaWd1cmUocDIyLCBuYW1lID0gIlBsYXRlbGV0X1BQQlAiKQpTYXZlRmlndXJlKHAyMywgbmFtZSA9ICJTQ0lTU09SU19DZWxsdHlwZXNfRmluYWwiKQpTYXZlRmlndXJlKHAyNCwgbmFtZSA9ICJNYXJrZXJfR2VuZXNfRm9yX1NDSVNTT1JTX0NlbGx0eXBlcyIsIGhlaWdodCA9IDgsIHdpZHRoID0gMTgpCmBgYAoKQW5kIG9mIGNvdXJzZToKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=